package com.fm.parser.xml;

import com.fm.base.factory.ClassFactory;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DefaultXMLHandler<T extends Map<String, Object>> implements XMLHandler {

	private int deepth = -1;
	private String tag = null;
	private ClassFactory<T> factory;
	private XMLReader<T> reader;
	private Stack<T> stack;
	private int currentDeepth = 0;

	private boolean inNeedTag = false;

	/**
	 * @param deepth 0 base
	 */
	public DefaultXMLHandler(int deepth, XMLReader<T> reader, ClassFactory<T> factory) {
		this.deepth = deepth;
		this.reader = reader;
		this.factory = factory;
		stack = new Stack<>();
	}

	public DefaultXMLHandler(String tag, XMLReader<T> reader, ClassFactory<T> factory) {
		this.tag = tag;
		this.reader = reader;
		this.factory = factory;
		stack = new Stack<>();
	}

	@Override
	public void doStartTag(String name, Map<String, String> attributes) {
		T map;
		if ((deepth != -1 && currentDeepth >= deepth) || inNeedTag) {
			map = factory.create();
			if (!attributes.isEmpty()) {
				map.put("attr", attributes);
			}
			stack.push(map);
		} else if (tag != null && tag.equals(name)) {
			inNeedTag = true;
			map = factory.create();
			if (!attributes.isEmpty()) {
				map.put("attr", attributes);
			}
			stack.push(map);
		}
		++currentDeepth;
	}

	@Override
	@SuppressWarnings("unchecked")
	public void doEndTag(String name) {
		--currentDeepth;
		if (deepth != -1 && currentDeepth == deepth) {
			reader.read(stack.pop());
		} else if (deepth != -1 && currentDeepth > deepth) {
			T json = stack.pop();
			Object list = stack.peek().get(name);
			if (list != null) {
				if (list instanceof List) {
					((List<T>) list).add(json);
				} else {
					throw new RuntimeException("属性:" + name + "与内置属性名冲突");
				}
			} else {
				List<T> array = new LinkedList<>();
				array.add(json);
				stack.peek().put(name, array);
			}
		} else if (tag != null && tag.equals(name)) {
			inNeedTag = false;
			reader.read(stack.pop());
		} else if (tag != null && !tag.equals(name) && inNeedTag) {
			T document = stack.pop();
			Object list = stack.peek().get(name);
			if (list != null) {
				if (list instanceof List) {
					((List<T>) list).add(document);
				} else {
					throw new RuntimeException("属性:" + name + "与内置属性名冲突");
				}
			} else {
				List<T> array = new LinkedList<>();
				array.add(document);
				stack.peek().put(name, array);
			}
		}
	}

	@Override
	public void characters(String str) {
		Pattern p = Pattern.compile("[^ \t\r\n]+");
		Matcher m = p.matcher(str);
		if (m.find()) {
			T json = stack.peek();
			json.put("text", str);
		}
	}
}
